/*
 * Copyright 2020-2025 the original author or authors.
 * You cannot use this file unless authorized by the author.
 */
package org.ipig.commons.annotation;

import org.springframework.beans.factory.annotation.AnnotatedBeanDefinition;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.BeanNameGenerator;
import org.springframework.core.annotation.AnnotationAttributes;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.util.StringUtils;

import java.util.Map;
import java.util.Set;

/**
 * GenericBeanNameGenerator
 *
 * @author <a href="mailto:comchnts@163.com">chinats</a>
 * @version $$Id: GenericBeanNameGenerator.java 6 2020-04-03 11:25:10Z ts <comchnts@163.com> $$
 * @since 1.0
 */
public class GenericBeanNameGenerator implements BeanNameGenerator {
    private static final String COMPONENT_ANNOTATION_CLASSNAME = "org.springframework.stereotype.Component";
    private static final String JAVAX_MANAGEDBEAN_ANNOTATION_CLASSNAME = "javax.annotation.ManagedBean";
    private static final String JAVAX_NAMED_ANNOTATION_CLASSNAME = "javax.inject.Named";
    private static final String KEY_VALUE = "value";

    public GenericBeanNameGenerator() {

    }
    /**
     * 如果spring bean 有自定义的名称，就使用自定义名称，如果没有则使用类的全限定名
     * @param definition BeanDefinition
     * @param registry   BeanDefinitionRegistry
     * @return String
     * @see org.springframework.beans.factory.support.BeanNameGenerator#generateBeanName(org.springframework.beans.factory.config.BeanDefinition,
     * org.springframework.beans.factory.support.BeanDefinitionRegistry)
     */
    public String generateBeanName(BeanDefinition definition, BeanDefinitionRegistry registry) {
        if (definition instanceof AnnotatedBeanDefinition) {
            String beanName = determineBeanNameFromAnnotation((AnnotatedBeanDefinition) definition);
            if (StringUtils.hasText(beanName)) {
                return beanName;
            }
        }
        return definition.getBeanClassName();
    }

    /**
     * @param annotatedDef
     * @return
     */
    protected String determineBeanNameFromAnnotation(AnnotatedBeanDefinition annotatedDef) {
        AnnotationMetadata amd = annotatedDef.getMetadata();
        String beanName = null;
        for (String type : amd.getAnnotationTypes()) {
            AnnotationAttributes annAtts = null;
            Map<String, Object> map = amd.getAnnotationAttributes(type, false);
            if (map != null) {
                if (map instanceof AnnotationAttributes) {
                    annAtts = (AnnotationAttributes) map;
                } else {
                    annAtts = new AnnotationAttributes(map);
                }
            }
            Set<String> metaAnnTypes = amd.getMetaAnnotationTypes(type);
            if ((type.equals(COMPONENT_ANNOTATION_CLASSNAME)
                    || type.equals(JAVAX_MANAGEDBEAN_ANNOTATION_CLASSNAME)
                    || type.equals(JAVAX_NAMED_ANNOTATION_CLASSNAME)
                    || (metaAnnTypes != null && metaAnnTypes.contains(COMPONENT_ANNOTATION_CLASSNAME)))
                    && (annAtts != null && annAtts.containsKey(KEY_VALUE))) {
                Object value = annAtts.get(KEY_VALUE);
                if (value instanceof String) {
                    String strVal = (String) value;
                    if (StringUtils.hasLength(strVal)) {
                        if (beanName != null && !strVal.equals(beanName)) {
                            throw new IllegalStateException("Stereotype annotations suggest inconsistent " +
                                    "component names: '" + beanName + "' versus '" + strVal + "'");
                        }
                        beanName = strVal;
                    }
                }
            }
        }
        return beanName;
    }
}
